<template>
  <el-select
    ref="treeSelect"
    :value="checkedValue"
    placeholder="请选择"
    :clearable="clearable"
    :disabled="disabled"
    :multiple="multiple"
    :collapse-tags="collapseTags"
    class="input-tree-select h36"
    popper-class="tree-select-wrapper-popper"
    @clear="clearHandle"
    @focus="initHandle"
    @remove-tag="removeTag"
    @visible-change="visiblChange"
  >
    <div class="search-input-wrap bgf w100">
      <el-input
        ref="inputSearch"
        v-model="filterText"
        class="search-input"
        placeholder="请输入关键字进行搜索"
        @keyup.native="doSearch"
      />
    </div>

    <!-- 为了实现多选，才走的这一步 -->
    <el-option
      v-for="item in selectOptions"
      :key="item[assignProps.value]"
      style="display: none"
      :label="item[assignProps.label]"
      :value="item[assignProps.value]"
    />

    <el-option v-loading="optionLoading" value="">
      <el-tree
        id="tree-option"
        ref="selectTree"
        :accordion="accordion"
        :data="['area', 'organize'].includes(type) ? treeOptions : options"
        :props="assignProps"
        :default-expanded-keys="defaultExpandedKeys"
        :node-key="assignProps.value"
        :highlight-current="true"
        :expand-on-click-node="expandOnClickNode || multiple"
        :default-expand-all="false"
        :default-checked-keys="defaultCheckedKeys"
        :filter-node-method="filterNode"
        :current-node-key="valueId"
        :load="loadNode"
        :lazy="lazy"
        :show-checkbox="multiple"
        @node-click="handleNodeClick"
        @check="handelTreeChange"
      />
    </el-option>
  </el-select>
</template>

<script>
import { OrganizeSearchOrg, OrganizeSimpleTree } from "api/sys/organize";
import { AreaTree, AreaSearch } from "api/sys/area";
import { debounce, cloneDeep } from "lodash";
import scrollIntoView from "libs/scroll-into-view";
import emitter from "element-ui/src/mixins/emitter";

// 默认配置
const DEFAULT_PROPS = {
  value: "id", // ID字段名
  label: "title", // 显示名称
  children: "childList", // 子级字段名
  // 这里的改动可能会影响到全局
  isLeaf: "leaf",
};

// 部门配置
const ORGANIZE_COFIG = {
  value: "id",
  label: "name",
};

// 地区配置
const AREA_COFIG = {
  value: "areaId",
  label: "name",
};

export default {
  name: "InputTreeSelect",
  mixins: [emitter],
  model: {
    event: "change",
  },
  props: {
    // 树选择框类型，default config：area=>地区,organize=>部门
    type: {
      type: String,
      default: "custom",
    },
    // 配置项
    props: {
      type: Object,
    },
    // 初始值(emit a json string)
    value: {
      type: [String, Number],
      default: "",
    },
    // 是否可清空
    clearable: {
      type: Boolean,
      default: true,
    },
    // 自动收起
    accordion: {
      type: Boolean,
      default: false,
    },
    placeholder: {
      type: String,
      default: "",
    },
    // 懒加载
    lazy: {
      type: Boolean,
      default: true,
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false,
    },
    // 默认展开项
    // defaultExpandedkeys: {
    //   type: Array,
    //   default: () => [],
    // },
    expandOnClickNode: {
      type: Boolean,
      default: false,
    },
    // 获取树形数据API
    loadTreeApi: {
      type: Function,
    },
    // 树搜索API
    searchApi: {
      type: Function,
      default: () => {},
    },
    // 搜索的参数
    searchParamsKey: {
      type: String,
      default: () => {},
    },
    // 默认的父级id
    parentId: {
      type: [Number, String],
      default: 0,
    },
    // 是否多选,多选默认设置expand-on-click-node为true,方便数据点击
    multiple: {
      type: Boolean,
      default: false,
    },
    // 是否折叠显示
    collapseTags: {
      type: Boolean,
      default: false,
    },
    // 默认的属性结构，非懒加载
    options: {
      type: Array,
      default: () => [],
    },
    // 加载树数据额外查询参数
    otherParams: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      // 搜索字段
      filterText: "",
      // 树形区域加载
      optionLoading: false,
      // 树数据列表
      treeOptions: [],
    };
  },
  computed: {
    // 简化props写法
    assignProps() {
      return Object.assign(
        {},
        DEFAULT_PROPS,
        (() => {
          if (this.props) return this.props;
          switch (this.type) {
            case "organize":
              return ORGANIZE_COFIG;
            case "area":
              return AREA_COFIG;
            default:
              return this.props;
          }
        })()
      );
    },
    loadTreeApiFormat() {
      if (this.loadTreeApi) return this.loadTreeApi;
      switch (this.type) {
        case "organize":
          return OrganizeSimpleTree;
        case "area":
          return AreaTree;
        default:
          return this.loadTreeApi;
      }
    },
    searchApiFormat() {
      switch (this.type) {
        case "organize":
          return OrganizeSearchOrg;
        case "area":
          return AreaSearch;
        default:
          return this.searchApi;
      }
    },
    searchParamsKeyFormat() {
      switch (this.type) {
        case "organize":
          return "organizeName";
        case "area":
          return "areaName";
        default:
          return this.searchParamsKey;
      }
    },
    checkedValue() {
      const { value } = this.assignProps;
      if (this.multiple) {
        if (!this.selectOptions.length) return [];
        return this.selectOptions.map((item) => +item[value]);
      } else {
        if (!this.selectOptions.length) return "";
        return this.selectOptions[0][value];
      }
    },
    selectOptions() {
      if (!this.value) return [];
      return this.getFormatedValue();
    },
    // 默认展开项
    defaultExpandedKeys() {
      if (this.lazy) return [];
      const arr = this.selectOptions.map((item) => item[this.assignProps.value] + "");
      return arr || [];
    },
    valueId() {
      if (!this.selectOptions.length) return "";
      return this.selectOptions[0][this.assignProps.value];
    },
    defaultCheckedKeys() {
      if (!this.selectOptions.length) return [];
      return this.selectOptions.map((item) => item[this.assignProps.value]);
    },
  },
  watch: {
    value(val) {
      // this.initHandle();
      this.dispatch("ElFormItem", "el.form.change", val);
    },
    treeOptions: {
      deep: true,
      handler() {
        this.initHandle();
      },
    },
  },
  async mounted() {
    await this.$nextTick();
    this.initHandle();
  },
  methods: {
    /**
     * 初始化树形控件
     */
    async initHandle() {
      if (!this.multiple) {
        if (this.valueId) {
          let node = this.$refs.selectTree.getNode(this.valueId);
          if (node !== null) {
            this.setCurrentKey();
          }
        }
      } else {
        await this.$nextTick();
        this.$refs.selectTree.setCheckedKeys(this.defaultCheckedKeys);
      }
    },
    /**
     * 获取格式化后的数据
     */
    getFormatedValue() {
      let res = [];
      const arr = JSON.parse(this.value);
      arr.map((item) => {
        const { label, value: propsValue } = this.assignProps;
        const { name, value } = item;
        res.push({ [propsValue]: +value, [label]: name });
      });
      return res;
    },
    /**
     * 设置默认选中
     */
    async setCurrentKey() {
      await this.$nextTick();
      if (this.$refs.selectTree && this.valueId) {
        this.$refs.selectTree.setCurrentKey(this.valueId);
      }
    },
    /**
     * 搜索
     */
    doSearch() {
      this.lazy ? this.doAsyncSearch() : this.doLocalSearch();
    },
    /**
     * 远程异步搜索
     */
    doAsyncSearch: debounce(async function () {
      try {
        this.optionLoading = true;
        if (this.filterText) {
          // 正常加载
          this.$refs.selectTree.store.lazy = false;
          this.$refs.selectTree.store.defaultExpandAll = true;
          await this.asyncSearch({
            [this.searchParamsKeyFormat]: this.filterText,
            parentId: this.parentId,
          });
        } else {
          this.loadTreeData();
        }
      } catch (e) {
        console.log(e);
      } finally {
        this.optionLoading = false;
      }
    }, 300),
    /**
     * 本地搜索
     */
    doLocalSearch() {
      this.$refs.selectTree.filter(this.filterText);
    },
    /**
     * 加载树形数据
     */
    async loadTreeData() {
      // 懒加载
      this.$refs.selectTree.store.lazy = true;
      this.$refs.selectTree.store.defaultExpandAll = false;
      const treeData = await this.getTreeData();
      this.treeOptions = treeData;
    },
    /**
     * 搜索所属部门
     */
    async asyncSearch(params) {
      try {
        const data = await this.searchApiFormat(params);
        this.searchedTreeDataFormat(data);
        // 更新树形数据
        this.treeOptions = data;
      } catch (e) {
        console.log(e);
      }
    },
    /**
     * 格式化搜索的数据
     */
    searchedTreeDataFormat(data) {
      data.map((item) => {
        const { subCount, childList } = item;
        item.leaf = !subCount;
        if (childList && childList.length) {
          this.searchedTreeDataFormat(childList);
        }
      });
    },
    /**
     * 节点点击事件
     */
    handleNodeClick(node) {
      if (!this.multiple) {
        const { subCount, fullName } = node;
        const { label, value } = this.assignProps;
        if (!this.expandOnClickNode || !subCount) {
          this.$emit(
            "change",
            JSON.stringify([
              {
                name: node[label],
                value: node[value],
                ...(this.type === "area" && { fullName }),
              },
            ])
          );
          // 自动隐藏
          this.$refs.treeSelect.blur();
        }
      }
    },
    /**
     * 清除选中(点击清空按钮或者重置)
     */
    clearHandle() {
      this.lazy && this.collapseAll();
      this.doReset();
      this.$emit("change", "");
    },
    /**
     * 重置
     */
    doReset() {
      this.activeNode = "";
      if (this.multiple) {
        this.$refs.selectTree.setCheckedKeys([]);
      } else {
        this.$refs.selectTree.setCurrentKey("");
      }
    },
    /**
     * 全部折叠
     */
    collapseAll() {
      for (let i = 0; i < this.treeOptions.length; i++) {
        const {
          store: { nodesMap },
        } = this.$refs.selectTree;
        nodesMap[this.treeOptions[i].id].expanded = false;
      }
    },
    /**
     * 懒加载数据
     */
    async loadNode(node, resolve) {
      let defautVal = this.parentId;
      const { data, level } = node;
      if (level !== 0) {
        defautVal = data[this.assignProps.value];
      } else {
        // 存储第一层的node、resolve，用于type===1时，手动刷新数据
        this.node = node;
        this.resolve = resolve;
      }
      const tempData = await this.getTreeData({
        parentId: defautVal,
        parentName: data.fullName || data.name,
      });
      resolve(tempData);
      // 存储第一级数据，不用每次刷新都重新加载
      if (level === 0) {
        this.firstTreeData = cloneDeep(tempData);
      }
      await this.$nextTick();
      if (this.multiple) {
        this.$refs.selectTree.setCheckedKeys(this.defaultCheckedKeys);
      } else {
        this.setCurrentKey();
      }
    },
    /**
     * 获取树形数据(其他的数据加载，在组件中重新定义)
     */
    async getTreeData({ parentId = 0, parentName = "" } = {}) {
      try {
        const data = await this.loadTreeApiFormat(parentId || this.parentId, this.otherParams);
        let tempData = [];
        if (data && data.length) {
          data.map((item) => {
            const { name, subCount } = item;
            item.leaf = !subCount;
            item.fullName = parentName + name;
          });
          tempData = data;
        }
        return tempData;
      } catch (e) {
        console.log(e);
        return [];
      }
    },
    /**
     * popper显示隐藏
     */
    visiblChange(bool) {
      if (!bool) {
        // popper隐藏，恢复原始树形数据
        if (this.filterText) {
          // 清空搜索的值
          this.filterText = "";
          // 清空选中的节点
          this.activeNode = "";
          if (this.lazy) {
            // 设置回懒加载
            this.$refs.selectTree.store.lazy = true;
            // 设置回不全展开
            this.$refs.selectTree.store.defaultExpandAll = false;
            // 手动刷新数据
            this.node.childNodes = [];
            this.resolve(this.firstTreeData);
            this.setCurrentKey();
          } else {
            setTimeout(() => {
              this.doLocalSearch();
            }, 0);
          }
        }
      } else {
        !this.checkedValue && this.doReset();
        // popper显示，控制scrollbar的位置
        setTimeout(() => {
          this.setActiveDataScrollIntoView();
          this.$refs.inputSearch.focus();
        }, 100);
      }
    },
    /**
     * 将选中项滚动到可见区
     */
    async setActiveDataScrollIntoView() {
      await this.$nextTick();
      if (this.$refs.treeSelect) {
        // TODO: 这里还有待优化
        // 单选和多选获取dom的方式不一样
        const parentDom = this.$refs.treeSelect.$children[this.multiple ? 2 : 1].$children[0];
        // const parentDom = this.$refs.treeSelect.$children[1].$children[0];
        this.getActiveDom(this.$refs.selectTree.$children);
        const container = parentDom.$el.querySelector(".el-select-dropdown__wrap");
        // 取至element-ui源码中的方法
        scrollIntoView({
          container,
          selected: this.activeNode,
          paddingTop: 46,
        });
        parentDom.handleScroll();
      }
    },
    /**
     * 递归获取当前active的dom
     */
    getActiveDom(arr) {
      for (let i = 0; i < arr.length; i++) {
        const { $el, $children } = arr[i];
        const classList = Object.values($el.classList);
        if (classList.includes(this.multiple ? "is-checked" : "is-current")) {
          this.activeNode = $el;
          !this.multiple && $el.classList.add("is-current");
          break;
        }
        if ($children.length) {
          this.getActiveDom($children);
        }
      }
    },
    /**
     * 过滤节点
     */
    filterNode(value, data) {
      if (!value) return true;
      return data[this.assignProps.label].indexOf(value) !== -1;
    },
    /**
     * 多选的选择事件
     */
    handelTreeChange() {
      if (!this.multiple) return;
      this.activeNode = "";
      const arr = this.getSimpleCheckedNodes(this.$refs.selectTree.store);
      this.$emit("change", this.getSubmitValue(arr));
    },
    /**
     * 获取提交的数据
     */
    getSubmitValue(arr) {
      let tempArr = [];
      arr.map((item) => {
        const { fullName } = item;
        const { label, value } = this.assignProps;
        tempArr.push({
          name: item[label],
          value: item[value],
          ...(this.type === "area" && { fullName }),
        });
      });
      return tempArr.length ? JSON.stringify(tempArr) : "";
    },
    /**
     * 子级全选中时，只传父级id
     */
    getSimpleCheckedNodes(store) {
      const checkedNodes = [];
      const traverse = (node) => {
        const childNodes = node.root ? node.root.childNodes : node.childNodes;
        childNodes.forEach((child) => {
          if (child.checked) {
            checkedNodes.push(child.data);
          }
          if (child.indeterminate) {
            traverse(child);
          }
        });
      };
      traverse(store);
      return checkedNodes;
    },
    /**
     * 多选删除
     */
    removeTag(id) {
      const arr = this.getFormatedValue();
      const tempArr = arr.filter((item) => +item[this.assignProps.value] !== +id);
      this.$emit("change", this.getSubmitValue(tempArr));
    },
  },
};
</script>

<style lang="scss" scoped>
.tree-select-wrapper-popper {
  .el-scrollbar__view {
    position: relative;
  }
  .search-input-wrap {
    position: absolute;
    top: 0;
    left: 0;
    padding: 5px 10px;
    width: 100%;
    z-index: 1;
  }

  .el-select-dropdown__wrap {
    .el-select-dropdown__list {
      padding-top: 0;
    }
  }

  .el-select-dropdown__item {
    height: 100%;
    padding: 0;
    padding-top: 40px;
    background: #fff;
    &.selected {
      font-weight: normal;
    }
    .el-tree {
      ::v-deep .el-tree-node__content {
        padding-right: 15px;
      }
      ::v-deep .is-current {
        .el-tree-node__label {
          color: #409eff;
        }
        .el-tree-node__children {
          .el-tree-node__label {
            color: #606266;
            padding-right: 10px;
            font-weight: normal;
          }
        }
      }
    }
  }
}

::v-deep .el-checkbox .el-checkbox__inner {
  z-index: 0 !important;
}
</style>
